/**********************************************************************\

 Copyright 2012 Diggory Blake

 This file is part of klip-engine

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.

\**********************************************************************/

#include "kengine.h"
#include "kcomponent.h"
#include "ksystem.h"
#include "kgraphics.h"
#include "kresources.h"
#include "kanimation.h"
#include "klog.h"
#include "kperformance.h"
#include "kstate.h"

kEngine::kEngine() {
	_system = 0;
	_graphics = 0;
	_resources = 0;
	_animation = 0;
	_log = 0;
	_hasQuit = false;
	_fpsAcc = 0;
	_fps = 0;
	_timeAcc = 0.0f;
}
kEngine* kEngine::create(INIT_LIST2(kComponent* component)) {
	DEF_LIST(kComponent*, component);

	kEngine* result = new kEngine();

	for (int i = 0; i < INIT_N; i++) {
		kComponent* component = GET_ARG(component, i);
		if (component) {
			result->_components.push_back(component);

			// Find a system component
			if (!result->_system && component->isA<kSystem*>()) {
				result->_system = component->as<kSystem*>();
			}

			// Find a graphics component
			if (!result->_graphics && component->isA<kGraphics*>()) {
				result->_graphics = component->as<kGraphics*>();
			}

			// Find a resources component
			if (!result->_resources && component->isA<kResources*>()) {
				result->_resources = component->as<kResources*>();
			}

			// Find an animation component
			if (!result->_resources && component->isA<kAnimation*>()) {
				result->_animation = component->as<kAnimation*>();
			}

			// Find a log component
			if (!result->_log && component->isA<kLog*>()) {
				result->_log = component->as<kLog*>();
			}
		}
	}

	for (auto i = result->_components.begin(); i != result->_components.end(); i++)
		(*i)->onAttached(result);

	result->_startTime = result->_system->getTime();
	result->_totalTime = 0;

	return result;
}
bool kEngine::update() {
	PERF_QUERY;

	// Calculate delta time in seconds
	unsigned newTime = _system->getTime() - _startTime;
	_deltaTime = (newTime - _totalTime)*0.001f;
	_totalTime = newTime;

	_fpsAcc++;
	_timeAcc += _deltaTime;
	if (_timeAcc >= 1.0f) {
		_timeAcc -= 1.0f;
		_fps = _fpsAcc;
		_fpsAcc = 0;
	}
	
	for (auto i = _components.begin(); i != _components.end(); i++)
		(*i)->onUpdate(_deltaTime);

	if (!_hasQuit) {
		unsigned size = _states.size();
		for (unsigned i = 0; (i < size) && (i < _states.size()); i++)
			_states[i]->update(i == size-1, _deltaTime);

		while (!_stateTransition.empty()) {
			_stateTransition.front()();
			_stateTransition.pop();
		}
	} else {
		while (!_states.empty()) {
			delete _states.back();
			_states.pop_back();

			if (!_states.empty())
				_states.back()->onPopChild();
		}
	}

	return !_hasQuit;
}
void kEngine::pushState(std::function<kState* ()> state) {
	_stateTransition.push([this, state](){
		if (!_states.empty())
			_states.back()->onPushChild();

		_states.push_back(state());
	});
}
void kEngine::popState() {
	_stateTransition.push([this](){
		delete _states.back();
		_states.pop_back();

		if (!_states.empty())
			_states.back()->onPopChild();
	});
}
